using UnityEngine;
using System.Collections;
using System.Collections.Generic;

// ************************************************************************************************
// Exemple d'Init du lwLangManager
// ************************************************************************************************
// m_langManager = lwLangManager.GetInstance();
// m_langManager.SetGameName( VersionName ); // optionnel, si pas de déclinaison du jeu, ne pas appeler cette fonction
// m_langManager.AddTextFile( "Texts/CommonTexts" );
// m_langManager.AddTextFile( "Specific/Titanic/Texts/Texts" );
// m_langManager.AddFont( "SmallFont" );
// m_langManager.AddFont( "Main" );
// m_langManager.AddFont( "TitleFont" );
// m_langManager.SetInitLanguage( CurrentLanguage );
// ************************************************************************************************
// Le projet doit avoir un fichier langs.enc dans le dossier Unity/External
// lors du build, ce fichier doit également être copié dans le sous-dossier External
// le fichier "enc" est un fichier texte encodé contenant la liste des langages gérés par le build
// si ce fichier contient des langues exotiques (CN, KR, RU, JP), le système de chargement de fonts sera actif
// les fonts (texture et fichier txt contenant les UVs) doivent être présentes dans des assetsbundles :
// Fonts.assetbundle contient les fonts latines utilisées pour toutes langues non exotiques
// Pour chaque autre langue on doit avoir Fonts_XX.assetbundle où XX correspond à la langue
//
// Les fichiers de textes doivent être soit contenus dans un assetbundle nommé Texts.assetbundle,
// soit dans le dossier Resources du projet. (Voir plus loin (AddTextFile) pour la liste des fichiers)
// ************************************************************************************************

public delegate void lwLanguageChangedDelegate();

public class lwLangManager : MonoBehaviour
{
	private static lwLangManager m_instance = null;

	private int m_nCurrentLanguage = -1;
	private int m_nInitLanguage = 0;
	private bool  m_bIsInitialized = false;
	public bool IsInitialized { get { return m_bIsInitialized; } }
	
	private lwLanguageChangedDelegate m_languageDelegate;

	private List<string> m_langNames;
	private List<SystemLanguage> m_langId;
	private List<Dictionary<string,string>> m_texts;

	private Dictionary<string, Texture> m_fontsTexture = null;
	private Dictionary<string, TextAsset> m_fontsText = null;

	private static string[] latinLangs = { "EN", "FR", "SP", "GE", "IT", "NL", "PO", "SW" };

	private bool m_bExoticBuild = false;
	public bool IsExoticBuild { get { return m_bExoticBuild; } }
	private bool m_bExoticFont = false;
	public bool IsExoticFontLoaded { get { return m_bExoticFont; } }
	
	private List<string> m_sFontNameArray = new List<string>();
	private List<string> m_sTextFilePathArray = new List<string>();
	
	// -------------------------------------------------
	// Asset bundle contenant les fichiers texte
	// si cet assetbundle n'est pas chargé, les fichiers 
	// seront lus depuis les Resources
	// -------------------------------------------------
	private AssetBundle m_pTextAssetBundle = null;
	private string m_sGameName = "";
	
	// ************************************************************************************************
	static public lwLangManager GetInstance()
	{
		if( m_instance==null)
		{
			GameObject timeObject = new GameObject( "lwLangManager" );
			m_instance = timeObject.AddComponent<lwLangManager>();
		}
		return m_instance;
	}
	
	// Ajoute une font à la liste des fonts gérées par le mode exotic
	public void AddFont( string sFontName )
	{
		m_sFontNameArray.Add( sFontName );
	}
	
	// ************************************************************************************************
	// Ajoute un fichier contenant des textes
	// le fichier doit avoir une extension .txt
	// le Path est un Path relatif à Unity/Assets et ne comportant pas l'extension
	// exemple : Texts/CommonTexts chargera le fichier Unity/Assets/Texts/CommonTexts.txt
	// le fichier doit être au format csv en UTF16 avec séparateur ; 
	// la première ligne contient les noms de langues traitées (2 caractères par langage)
	// Exemple de première ligne : 
	//		﻿ID;EN;FR;SP;GE;IT;NL
	// la première colonne contient le TextID les suivantes les traductions dans l'ordre indiqué par la première ligne
	// ATTENTION : l'ordre d'ajout des TextFiles est important
	// si un même TextID se trouve dans plusieurs d'entre eux, le dernier chargé aura raison...
	// Les fichiers doivent être présent soit :
	//		- dans l'assetbundle nommé Texts.assetbundle
	//		- dans le dossier Resources compilé avec le projet
	//		- en clair dans un sous-dossier "Texts", ce qui permet de tester des trads sans rien rebuilder
	// ************************************************************************************************
	public void AddTextFile( string sPath )
	{
		m_sTextFilePathArray.Add( sPath );
	}
	
	// Set the starting language
	public void SetInitLanguage( int nLanguageId )
	{
		m_nInitLanguage = nLanguageId;
	}
	
	public void SetGameName( string sGameName )
	{
		m_sGameName = sGameName;
	}
	
	// ************************************************************************************************
	private void OnDestroy()
	{
		if ( m_instance == this )
		{
			m_bIsInitialized = false;
			m_langNames = null;
			m_langId = null;
			m_texts = null;
		}

		if(m_bExoticBuild)
		{
			m_fontsText = null;
			m_fontsTexture = null;
		}

		m_instance = null;
		m_languageDelegate = null;
		
		m_sFontNameArray = null;
		m_sTextFilePathArray = null;
		
		if ( m_pTextAssetBundle != null )
		{
			m_pTextAssetBundle.Unload( true );
			m_pTextAssetBundle = null;
		}
	}
	
	private void AddLanguage( string sId, SystemLanguage nId )
	{
		m_langNames.Add( sId );
		m_langId.Add( nId );
		Debug.Log( "Adding language '" + sId + "' : " + nId.ToString() );
	}
	
	private void InitLanguageList()
	{
		m_langNames = new List<string>();
		m_langId = new List<SystemLanguage>();

		string sText = lwTools.LoadEncryptedTextFile( "Langs" );
		if ( string.IsNullOrEmpty( sText ) )
			sText = lwTools.LoadTextFile( "Texts/Langs", "txt", null );
		if ( string.IsNullOrEmpty( sText ) )
			sText = lwTools.LoadTextFile( "Texts/Langs");
		if ( string.IsNullOrEmpty( sText ) )
			Debug.LogError( "lwLangManager : No langs.txt/enc file found !" );
		string [] sLines = sText.Split('\n');
		foreach (string sLine in sLines)
		{		
			if ( string.IsNullOrEmpty( sLine ))
				break;
			
			string sId = sLine.Substring(0, 2);
			SystemLanguage nId = SystemLanguage.Unknown;
			switch( sId )
			{
			case "ID":
				continue;
			case "EN":
				nId = SystemLanguage.English;
				break;
			case "FR":
				nId = SystemLanguage.French;
				break;
			case "IT":
				nId = SystemLanguage.Italian;
				break;
			case "GE":
				nId = SystemLanguage.German;
				break;
			case "SP":
				nId = SystemLanguage.Spanish;
				break;
			case "NL":
				nId = SystemLanguage.Dutch;
				break;
			case "PO":
				if(lwBuildSettings.IsExoticBuild())
				{
					m_bExoticBuild = true;
					nId = SystemLanguage.Portuguese;
				}
				break;
			case "SW":
				if(lwBuildSettings.IsExoticBuild())
				{
					m_bExoticBuild = true;
					nId = SystemLanguage.Swedish;
				}
				break;
			case "RU":
				if(lwBuildSettings.IsExoticBuild())
				{
					m_bExoticBuild = true;
					nId = SystemLanguage.Russian;
				}
				break;
			case "CN":
				if(lwBuildSettings.IsExoticBuild())
				{
					m_bExoticBuild = true;
					nId = SystemLanguage.Chinese;
				}
				break;
			case "JP":
				if(lwBuildSettings.IsExoticBuild())
				{
					m_bExoticBuild = true;
					nId = SystemLanguage.Japanese;
				}
				break;
			case "KR":
				if(lwBuildSettings.IsExoticBuild())
				{
					m_bExoticBuild = true;
					nId = SystemLanguage.Korean;
				}
				break;
			default:
				Debug.LogError("Unknown language : " + sLine);
				continue;
			}
			if(nId != SystemLanguage.Unknown)
				AddLanguage( sId, nId );
		}
	}
	
	void Awake()
	{
		if ( m_instance	!= null && m_instance != this )
		{
			Destroy(gameObject);
		}
		else
		{
			m_instance = this;
			GameObject.DontDestroyOnLoad( gameObject );
		}
	}
	
	private IEnumerator Start()
	{
		if( !m_bIsInitialized )
		{
			// Read Langs.txt
			InitLanguageList();

			// Load Texts AssetBundle
			yield return StartCoroutine( InitTextsAssetBundle() );
			
			m_texts = new List<Dictionary<string,string>>();
			
			// Assets des fonts
			if(m_bExoticBuild)
			{
				m_fontsTexture = new Dictionary<string, Texture>();
				m_fontsText = new Dictionary<string, TextAsset>();
			}
			
			List<string[]> sLinesArray = new List<string[]>();
			for( int i=0; i<m_sTextFilePathArray.Count; i++ )
			{
				sLinesArray.Add( LoadCSV( m_sTextFilePathArray[i] ) );
			}
			if ( m_pTextAssetBundle != null )
			{
				m_pTextAssetBundle.Unload( true );
				m_pTextAssetBundle = null;
			}
			
			//enumere les languages dispos dans Texts.txt
			string[] sColumns = null;
			// on recherche le fichier contenant le moins de colonnes pour s'en servir de référence
			for( int l=0; l<sLinesArray.Count; l++ )
			{
				if ( sLinesArray[l] != null )
				{
					string[] sTmpColumns = sLinesArray[l][0].Split( new char[]{ ';' } );
					if ( sColumns == null || (sTmpColumns != null && sTmpColumns.Length < sColumns.Length ) )
						sColumns = sTmpColumns;
				}
			}
			if ( sColumns != null )
			{
				// chargement des fichiers csv
				for( int i=0; i<m_langNames.Count; i++ )
				{
					m_texts.Add( new Dictionary<string,string>() );
					
					//cherche l'index de la colonne dans Texts.txt qui correspond au langage courant 
					int nColumnIndex=0;
					for ( nColumnIndex=0; nColumnIndex<sColumns.Length; nColumnIndex++)
					{
						if ( m_langNames[i].ToUpper() == sColumns[nColumnIndex].ToUpper())
							break;
					}
	
					if ( nColumnIndex<sColumns.Length && !string.IsNullOrEmpty(sColumns[nColumnIndex]) )
					{
						for( int l=0; l<sLinesArray.Count; l++ )
						{
							if ( sLinesArray[l] != null )
								ParseCSV( sLinesArray[l], nColumnIndex, m_texts[i] );
						}
					}
					else
					{
						Debug.LogWarning( "WARNING : Can't find langage '" + m_langNames[i] + "' in texts files." );
					}
				}
			}
			
			m_bIsInitialized = true;
			
			ChangeCurrentLanguage( m_nInitLanguage );
		}
	}
	
	private IEnumerator InitTextsAssetBundle()
	{
		if ( m_pTextAssetBundle == null && !string.IsNullOrEmpty(m_sGameName) )
		{
			string strPath = lwTools.GetAssetBundlePath("Texts", m_sGameName);
			WWW download = new WWW(strPath);
			yield return download;
			if ( string.IsNullOrEmpty( download.error ) )
		    {
				m_pTextAssetBundle = download.assetBundle;
				if ( m_pTextAssetBundle == null )
					Debug.LogError( "Error loading " + strPath );
				else
					Debug.Log( "AssetBundle loaded " + strPath );
			}
			download.Dispose();
		}
	}
	
	private IEnumerator InitFonts( bool bAsync )
	{
		string sLang = null;
		
		switch( GetCurrentLanguageName() )
		{
		case "JP":
		case "KR":
		case "CN":
		case "RU":
			sLang = "_" + GetCurrentLanguageName();
			m_bExoticFont = true;
			break;
			
		default:
			m_bExoticFont = false;
			sLang = "";
			break;
		}
		
		if ( sLang != null )
		{
			string sPath = lwTools.GetAssetBundlePath( "Fonts" + sLang, m_sGameName );
			Debug.Log( "Loading " + sPath );
			
			WWW download = new WWW( sPath );
			if( bAsync )
				yield return download;
			
			Texture[] tmpTexture = new Texture[m_fontsTexture.Count];
			int i = 0;
			
			foreach(string key in m_fontsTexture.Keys) {
				tmpTexture[i] = m_fontsTexture[key];
				i++;
			}
			
			m_fontsText.Clear();
			m_fontsTexture.Clear();
			
			foreach( string sName in m_sFontNameArray )
			{
				m_fontsTexture.Add( sName, download.assetBundle.Load( sName, typeof( Texture ) ) as Texture );
				m_fontsText.Add( sName, download.assetBundle.Load( sName, typeof( TextAsset ) ) as TextAsset );
			}
			
			download.assetBundle.Unload( false );
			download.Dispose();
			download = null;

			Resources.UnloadUnusedAssets();
		}
	}
	
	public int GetLanguageCount()
	{
		if ( m_langNames == null )
			return 0;
		else
			return m_langNames.Count;
	}
	
	public static bool isCurrentLanguageExotic()
	{
		string currentLanguage = GetInstance().GetCurrentLanguageName();
		foreach(string lang in latinLangs)
		{
			if(lang == currentLanguage)
				return false;
		}
		return true;
	}
	
	string[] LoadCSV( string sPath )
	{
		string[] sLineArray = null;
		string sTextFile = lwTools.LoadEncryptedTextFile( sPath );
		if (string.IsNullOrEmpty( sTextFile ))
			sTextFile = lwTools.LoadTextFile( sPath, ".txt", m_pTextAssetBundle );
		if (string.IsNullOrEmpty( sTextFile ))
			sTextFile = lwTools.LoadTextFile( sPath );
		
		if( string.IsNullOrEmpty(sTextFile))
		{
			Debug.LogWarning( sPath + " is missing !" );
		}
		else
		{
			sLineArray = sTextFile.Split( new char[]{ '\n', '\r' } );
		}
		return sLineArray;
	}

	void ParseCSV( string[] sLineArray, int nColumn, Dictionary<string,string> dictionnary )
	{
		for( int i=1; i<sLineArray.Length; i++ )
		{
			string sLine = sLineArray[i].TrimEnd( new char[] {'\r'} ).Replace( "\\n", "\n" );
			string[] sValues = sLine.Split( new char[]{ ';' } );
			
			if( sValues.Length>nColumn )
			{
				if(dictionnary.ContainsKey(sValues[0])) {
					dictionnary[sValues[0]] = sValues[nColumn];
				}
				else
					dictionnary.Add( sValues[0], sValues[nColumn] );
			}
		}
	}
	
	public void SetText( SpriteText spriteText, string sText )
	{
		if ( spriteText != null && sText != null )
		{
			SetCurrentLangFontForSpriteText( spriteText );
			spriteText.Text = sText;
		}
	}

	public string GetString( string sId )
	{
		string sText = string.Empty;
		
		if( m_texts!=null )
		{
			m_texts[m_nCurrentLanguage].TryGetValue( sId, out sText );
		}
//		Debug.Log( "GetString( " + sId  + " ) -> " + sText );
		if( sText==null )
			return string.Empty;
		else
			return sText;
	}
	
	/// <summary>
	/// Counts the string occurences.
	/// </summary>
	/// <returns>
	/// Number of string occurence.
	/// </returns>
	/// <param name='sId'>
	/// String identifier.
	/// </param>
	public int CountStringOccur( string sId )
	{
		int nStringOccur = 0;
		string[] sKeyArray;
		
		if( m_texts != null )
		{
			sKeyArray = new string[ m_texts[ m_nCurrentLanguage ].Keys.Count ];
			m_texts[m_nCurrentLanguage].Keys.CopyTo( sKeyArray, 0 );
			
			foreach( string text in sKeyArray )
				if( text.Contains( sId ) )
					nStringOccur++;
		}
		
		return nStringOccur;		
	}
	
	public string[] GetTextIdArray()
	{
		if (m_bIsInitialized)
		{
			List<string> list = new List<string>();
			Dictionary<string,string>.KeyCollection keys = m_texts[m_nCurrentLanguage].Keys;
			foreach( string k in keys )
			{
				list.Add( k );
			}
			list.Sort();
			return list.ToArray();
		}
		else
			return new string[0];
	}
	
	static public string GetLanguageName( int nIndex )
	{
		if ( m_instance	!= null && nIndex < m_instance.m_langNames.Count && nIndex >= 0 )
			return m_instance.m_langNames[nIndex];
		return string.Empty;
	}

	public string GetCurrentLanguageName()
	{
		return GetLanguageName(m_nCurrentLanguage);
	}

	public int GetCurrentLanguageId()
	{
		return m_nCurrentLanguage;
	}
	
	public TextAsset GetFontText( string sFontName )
	{
		TextAsset textAsset = null;
		
		m_fontsText.TryGetValue( sFontName, out textAsset );
		
		return textAsset;
	}
	
	public Texture GetFontTexture( string sFontName )
	{
		Texture texture = null;
		
		m_fontsTexture.TryGetValue( sFontName, out texture );
		
		return texture;
	}
	
	public int GetLanguageIndex( SystemLanguage nLang )
	{
		for( int i=0; i<m_langId.Count; i++ )
		{
			if ( m_langId[i] == nLang )
				return i;
		}
		return -1;
	}
	
	public void SetCurrentLangFontForSpriteText( SpriteText spriteText )
	{
		if(m_bExoticBuild)
		{
			if( spriteText.font.name == "TooltipFont" )
				return;
			
			Texture texture = GetFontTexture( spriteText.font.name );
			TextAsset textAsset = GetFontText( spriteText.font.name );
			
			if( texture != null && textAsset != null )
			{
				spriteText.renderer.material.mainTexture = texture;
				spriteText.SetFont( textAsset, spriteText.renderer.material );
			}
		}
	}
	
	public void ChangeCurrentLanguage( bool bForward )
	{
		ChangeCurrentLanguage( m_nCurrentLanguage+(bForward?1:-1) );
	}

	public void ChangeCurrentLanguage( int nId )
	{
		if ( m_bIsInitialized )
		{
			int nPreviousLanguage = m_nCurrentLanguage;
			m_nCurrentLanguage = nId;
			
			m_nCurrentLanguage = Mathf.Clamp( m_nCurrentLanguage, 0, GetLanguageCount()-1 );
					
			if( m_bExoticBuild && m_fontsText != null & m_fontsTexture != null 
				&& NeedChangeFont( GetLanguageName( nPreviousLanguage ), GetLanguageName( m_nCurrentLanguage ) ) )
					StartCoroutine(InitFonts());
			else if( m_languageDelegate!=null )
			{
				m_languageDelegate();
			}
		}
		else
			Debug.LogWarning( "lwLangManager.ChangeCurrentLanguage() called before init !" );
	}
	
	private IEnumerator InitFonts()
	{
		yield return StartCoroutine(InitFonts(true));
		if( m_languageDelegate!=null )
		{
			m_languageDelegate();
		}
	}
	
	private bool NeedChangeFont( string sLastLang, string sNewLang )
	{
		int nNumLangs = 0;
		
		foreach( string sLang in latinLangs )
			if( sLang == sLastLang || sLang == sNewLang )
				nNumLangs++;
		
		return nNumLangs < 2 ? true : false;
	}

	static public void RemoveLanguageDelegate( lwLanguageChangedDelegate LanguageChange )
	{
		if ( m_instance != null )
			m_instance.RemoveLanguageChangedDelegate( LanguageChange );
	}

	#region Delegates
	/// <summary>
	/// Sets the method to be called when language changed.
	/// NOTE: This will replace any and all delegates which have been set or added previously.  If you are unsure
	/// if any delegates are already registered, use AddInputDelegate() instead, or RemoveInputDelegate() to unset
	/// a previously registered delegate.  Only use this when you are sure you want to replace all previously registered delegates.
	/// </summary>
	/// <param name="del">A method that conforms to the LanguageChangedDelegate pattern.</param>
	public void SetLanguageChangedDelegate( lwLanguageChangedDelegate del )
	{
		m_languageDelegate = del;
	}

	/// <summary>
	/// Adds a method to be called when language changed.
	/// </summary>
	/// <param name="del">A method that conforms to the LanguageChangedDelegate pattern.</param>
	public void AddLanguageChangedDelegate( lwLanguageChangedDelegate del )
	{
		m_languageDelegate += del;
	}

	/// <summary>
	/// Removes a method added with AddInputDelegate().
	/// </summary>
	/// <param name="del">A method that conforms to the LanguageChangedDelegate pattern.</param>
	public void RemoveLanguageChangedDelegate( lwLanguageChangedDelegate del )
	{
		m_languageDelegate -= del;
	}
	#endregion
}
